﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;

using Atosenet.PluginEngine;


namespace Atosenet.IO
{
    [PluginAttribute("Atosenet", "Atosenet.IO.UdpServerIO", "UDP服务端连接", PluginType.IO)]
    internal class UdpServerIO : BaseIO
    {
        #region 成员

        private Socket _udpSocket;
        private Thread _udpThread;
        private IPEndPoint _localEP;

        #endregion


        #region 初始化

        public UdpServerIO()
            : base(UdpServerIOParameter.Default)
        { }

        public UdpServerIO(UdpServerIOParameter param)
            : base(param)
        { }

        #endregion


        #region 公开函数

        public override bool ApplyParameter()
        {
            try
            {
                if (IsOpened)
                    return false;

                _localEP = new IPEndPoint(IPAddress.Any, IOParam.ListeningPort);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public override bool Open()
        {
            try
            {
                if (IsOpened)
                    return false;

                _udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                _udpSocket.ReceiveBufferSize = 1024 * 128;
                _udpSocket.SendBufferSize = 1024 * 128;
                uint IOC_IN = 0x80000000;
                uint IOC_VENDOR = 0x18000000;
                uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
                _udpSocket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
                _udpSocket.Bind(_localEP);

                if (_udpThread == null || _udpThread.ThreadState != System.Threading.ThreadState.Running)
                {
                    _udpThread = new Thread(new ParameterizedThreadStart(ListeningProc));
                    _udpThread.IsBackground = true;
                    _udpThread.Name = "UdpServerIO_ListeningThread";
                    _udpThread.Start(_localEP);
                }

                OnIOOpened();
                return true;
            }
            catch
            {
                if (_udpThread != null && _udpThread.IsAlive)
                    _udpThread.Abort();
                return false;
            }
        }

        public override bool Close()
        {
            try
            {
                if (!IsOpened)
                    return false;

                _udpSocket.Shutdown(SocketShutdown.Both);
                _udpSocket.Close(5);
                CloseAllClients();

                if (_udpThread != null && _udpThread.IsAlive)
                    _udpThread.Abort();

                OnIOClosed();
                return true;
            }
            catch
            {
                return false;
            }
        }

        public override bool Close(int id)
        {
            if (id == ID)
                return Close();

            OnIOClosed(id);
            return Clients.Remove(id);
        }

        public override int WriteTo(int id, byte[] buffer, int offset, int length)
        {
            if (id == ID)
                return Write(buffer, offset, length);

            try
            {
                if(!IsOpened)
                    return -1;

                return Clients.Contains(id) ? _udpSocket.SendTo(buffer, offset, length, SocketFlags.None, Clients.Find(id).Address) : -1;
            }
            catch
            {
                return -1;
            }
        }

        public override int Write(byte[] buffer, int offset, int length)
        {
            try
            {
                if (!IsOpened)
                    return -1;

                for (int i = 0; i < Clients.Count; i++)
                {
                    _udpSocket.SendTo(buffer, offset, length, SocketFlags.None, Clients[i].Address);
                }
                return Clients.Count * length;
            }
            catch
            {
                return -1;
            }
        }

        #endregion


        #region 核心函数

        protected virtual void ListeningProc(object obj)
        {
            IPEndPoint ipEP = (IPEndPoint)obj;

            try
            {
                while (true)
                {
                    byte[] received = new Byte[1024 * 128];
                    EndPoint remoteEP = (ipEP);
                    int receivedCount = _udpSocket.ReceiveFrom(received, ref remoteEP);
                    byte[] data = new byte[receivedCount];
                    Array.Copy(received, 0, data, 0, data.Length);
                    int id = GetClientID((IPEndPoint)remoteEP);

                    OnRawDataReceived(data, id);
                }
            }
            catch
            {
                OnRawDataReceived(IOErrorType.ReadError);
            }
        }

        private int GetClientID(IPEndPoint address)
        {
            if (Clients.Contains(address))
            {
                return Clients.Find(address).ID;
            }
            else
            {
                int id = Clients.Add(address);
                OnIOOpened(id);
                return id;
            }
        }

        private void CloseAllClients()
        {
            for (int i = 0; i < Clients.Count; i++)
            {
                Close(Clients[i].ID);
            }
        }

        #endregion


        #region 属性

        public override IOModeType IOMode
        {
            get { return IOModeType.Server; }
        }

        public new UdpServerIOParameter IOParam
        {
            get { return base.IOParam as UdpServerIOParameter; }
        }

        #endregion
    }
}